<template>
	<div @click="goHome">About</div>
	<div id="any1">any 类型任意，不要过于依赖哦！</div>
</template>

<style scoped></style>

<script setup lang="ts">
import router from '@/router'
import { nextTick } from 'vue'
function goHome () {
    router.push('/')
}
/**
 * TS类型start
 */
const flag: boolean = true // 布尔
const count: number = 3 // 数字
const str: string = 'jj' // 字符
const enum Color {  // 枚举 加const后为常量枚举，与普通枚举的不同：常量枚举在编译阶段会被删除
    RED = '10',
    PINK = '粉色',
    BLUE = ''
}
const pink: Color = Color.PINK
const colors: Color[] = [Color.RED, Color.PINK, Color.BLUE]
console.log('枚举：', pink, colors)
const arr1: number[] = [1, 2, 3] // 数组
const arr2: Array<number> = [3, 2, 1]
console.log('数组: ', arr1, arr2)
const knowArr: [string, number] = ['a', 3] // 元组
console.log('元组类型-已知数量和类型的数组：', knowArr)
const sym1 = Symbol('hi') // Symbol [值唯一不变]
const sym2 = Symbol('hi')
console.log('Symbol 对比：', Symbol('hi') === Symbol('hi')) // = false
nextTick(() => {
    const any1: any = document.getElementById('any1') // any
    console.log('any: ', any1)
})
const n: null = null // null
const u: undefined = undefined // undefined
console.log('null & undefined: ', n, u)
let value: unknown                              // unknown：unknown类型会更加严格；unknown只能被赋值给unknown类型本身和any类型，any则啥都可以分配和被分配；
value = true
value = 3
// const valNew: string = value                 // Error
console.log('unknown: ', value)
function hello(name: string): void { // void 表示没有任何类型，函数无返回则TS会认为它的返回值是void类型
    console.log('hello fun: ', name)
}
hello('xiaofei')
try {
    function neverRech(): never {   // never 表示总是会抛出异常或者根本不会有返回值的函数的返回值类型
        throw new Error('an error')
    }
    neverRech()
} catch (e) {
    console.log('e: ', e)
}
let name: string | number // 联合类型
name = 1
console.log('联合类型name: ', name)
name = 'xiaofei'
console.log('联合类型name: ', name)

const someVal = 'this is a string'
const strLen: number = (<string>someVal).length         // 类型断言的两种形式，推荐as[比TS更确定你用的那个值的类型]
const strLen2: number = (someVal as string).length
console.log('类型断言：', strLen, strLen2)
try {
    let flag1: null | undefined | string
    // flag1.toString()                // 非空断言，报错l
    flag1!.toString()               // ! 非空断言，用于断言操作对象是非null、undefined类型
} catch (e) {
    console.log('e: ', e)
}

const flag11: "hello" = "hello";       // 3 种字面量类型：字符串字面量类型、数字字面量类型、布尔字面量类型
const flag21: 1 = 1;
const flag31: true = true;
console.log('字面量类型：', flag11, flag21, flag31)

type newType = number | string          // 类型别名
function say(hi: newType) {
    console.log('类型别名：', hi)
}
say('美女')
say(18)

type Flag1 = { x: string }
type Flag2 = Flag1 & { y: number }          // 交叉类型
const flag3: Flag2 = {
    x: 'xiaofei',
    y: 20
}
console.log('交叉类型：', flag3)
/**
 * TS类型end
 */

/**
 * TS函数start
 */
// 定义：可以制定参数类型和返回值类型
function callYou (name: string): void {
    console.log('I Called', name)
}
callYou('linhui')

// 函数表达式-定义函数类型
type SumFunc = (x: number, y: number) => number
const countNumber: SumFunc = function(a, b) {
    return a + b
}
console.log('定义函数类型: ', countNumber(3, 5))

// 函数可选参数，必须是最后一个参数
function print (name: string, age?: number): void {
    console.log('函数可选参数：', name, age)
}
print('zhihui')
print('zhangsan', 23)

// 函数默认参数
function ajax (url: string, method: string = 'GET'): void {
    console.log('函数默认参数：', url, method)
}
ajax('/upload')
ajax('/upload', 'POST')

// 函数剩余参数
function sum (...number: number[]) {
    return number.reduce((v, i) => v += i, 0)
}
console.log('函数剩余参数：', sum(2, 3, 4))

// 函数重载
// ...

/**
 * TS函数end
 */

/**
 * TS类start
 */
// 类的定义
class Person {
    public readonly name!: string;  // 初始属性没有加上值就需要加上 ! 
    constructor (name: string) {
        this.name = name
    }
    getName () {
        return this.name
    }
    changeName () {
        // this.name = 'after change'  // readonly，报错
    }
}
let p1 = new Person('hui')
console.log('类：', p1.getName())

// 存取器-转化为es5就是使用Object.defineProperty在类的原型上拦截属性对应的get/set方法
class User {
    myname!: string;
    constructor (myname: string) {
        this.myname = myname
    }
    get name() {    // 注意这里的是 name,是一个存取器，读写的是myname
        return this.myname;
    }
    set name(v) {
        this.myname = v
    }
}
let u1 = new User('zhihui')
u1.name = 'linhui'
console.log('类存取器：', u1.name)

/**
 * TS类start
 */

</script>
